﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Cysharp.Threading.Tasks;
using UniFramework.Asset;
using UnityEngine;
using UnityEngine.UI;
using YooAsset;

namespace UniFramework.Window
{
	public static class UniWindow
	{
		public struct WindowInfo
		{
			public string WindowName;
			public int WindowLayer;
			public bool IsLoadDone;
		}

		private static bool _isInitialize = false;
		private static GameObject _driver = null;
		private static readonly List<UIWindow> _stack = new List<UIWindow>(100);
		internal static GameObject Root { private set; get; }

		/// <summary>
		/// 初始化界面，创建基于Unity的驱动器
		/// </summary>
		/// <param name="root"></param>
		/// <exception cref="Exception"></exception>
		/// <exception cref="ArgumentNullException"></exception>
		public static void Initalize(GameObject root)
		{
			if (_isInitialize) throw new Exception($"{nameof(UniWindow)} is initialized !");
			if (root == null) throw new ArgumentNullException();
			if (_isInitialize == false)
			{
				// 创建驱动器
				_isInitialize = true;
				_driver = new UnityEngine.GameObject($"[{nameof(UniWindow)}]");
				_driver.AddComponent<UniWindowDriver>();
				UnityEngine.Object.DontDestroyOnLoad(_driver);
				UniLogger.Log($"{nameof(UniWindow)} initalize !");
				Root = root;
			}
		}

		/// <summary>
		/// 销毁界面系统
		/// </summary>
		public static void Destroy()
		{
			if (_isInitialize)
			{
				CloseAll();
				_isInitialize = false;
				if (_driver != null) GameObject.Destroy(_driver);
				UniLogger.Log($"{nameof(UniWindow)} destroy all !");
			}
		}

		/// <summary>
		/// 更新界面系统
		/// </summary>
		internal static void Update()
		{
			if (_isInitialize)
			{
				int count = _stack.Count;
				for (int i = 0; i < _stack.Count; i++)
				{
					if (_stack.Count != count)
						break;
					var window = _stack[i];
					window.InternalUpdate();
				}
			}
		}

		/// <summary>
		/// 设置屏幕安全区域（异形屏支持）
		/// </summary>
		/// <param name="safeRect">安全区域</param>
		public static void ApplyScreenSafeRect(Rect safeRect, UIWindow window)
		{
			CanvasScaler scaler = window.gameObject.GetComponent<CanvasScaler>();
			if (scaler == null)
			{
				UniLogger.Error($"Not found {nameof(CanvasScaler)} !");
				return;
			}

			var rectTransform = window.transform as RectTransform;
			float rateX = scaler.referenceResolution.x / Screen.width;
			float rateY = scaler.referenceResolution.y / Screen.height;
			float posX = (int)(safeRect.position.x * rateX);
			float posY = (int)(safeRect.position.y * rateY);
			float width = (int)(safeRect.size.x * rateX);
			float height = (int)(safeRect.size.y * rateY);

			float offsetMaxX = scaler.referenceResolution.x - width - posX;
			float offsetMaxY = scaler.referenceResolution.y - height - posY;

			// 注意：安全区坐标系的原点为左下角
			rectTransform.offsetMin = new Vector2(posX, posY); //锚框状态下的屏幕左下角偏移向量
			rectTransform.offsetMax = new Vector2(-offsetMaxX, -offsetMaxY); //锚框状态下的屏幕右上角偏移向量

			UniLogger.Log(
				$"{window.gameObject.name} Set Safe Rect,OffsetMin:{rectTransform.offsetMin},OffsetMax:{rectTransform.offsetMax}");
		}

		/// <summary>
		/// 模拟IPhoneX异形屏
		/// </summary>
		public static void SimulateIPhoneXNotchScreen(UIWindow window)
		{
			Rect rect;
			if (Screen.height > Screen.width)
			{
				// 竖屏Portrait
				float deviceWidth = 1125;
				float deviceHeight = 2436;
				rect = new Rect(0f / deviceWidth, 102f / deviceHeight, 1125f / deviceWidth, 2202f / deviceHeight);
			}
			else
			{
				// 横屏Landscape
				float deviceWidth = 2436;
				float deviceHeight = 1125;
				rect = new Rect(132f / deviceWidth, 63f / deviceHeight, 2172f / deviceWidth, 1062f / deviceHeight);
			}

			Rect safeArea = new Rect(Screen.width * rect.x, Screen.height * rect.y, Screen.width * rect.width,
				Screen.height * rect.height);
			ApplyScreenSafeRect(safeArea, window);
		}


		/// <summary>
		/// 获取窗口堆栈信息
		/// </summary>
		public static void GetWindowInfos(List<WindowInfo> output)
		{
			if (output == null)
				output = new List<WindowInfo>();
			else
				output.Clear();

			for (int i = 0; i < _stack.Count; i++)
			{
				var window = _stack[i];
				WindowInfo info = new WindowInfo();
				info.WindowName = window.WindowName;
				info.WindowLayer = window.WindowLayer;
				// info.IsLoadDone = window.IsLoadDone;
				output.Add(info);
			}
		}

		/// <summary>
		/// 获取所有层级下顶部的窗口名称
		/// </summary>
		public static string GetTopWindow()
		{
			if (_stack.Count == 0)
				return string.Empty;

			UIWindow topWindow = _stack[_stack.Count - 1];
			return topWindow.WindowName;
		}

		/// <summary>
		/// 获取指定层级下顶部的窗口名称
		/// </summary>
		public static string GetTopWindow(int layer)
		{
			UIWindow lastOne = null;
			for (int i = 0; i < _stack.Count; i++)
				if (_stack[i].WindowLayer == layer)
					lastOne = _stack[i];
			if (lastOne == null) return string.Empty;
			return lastOne.WindowName;
		}


		/// <summary>
		/// 查询窗口是否存在
		/// </summary>
		public static T HasWindow<T>() where T : UIWindow
		{
			return HasWindow(typeof(T)) as T;
		}

		private static UIWindow HasWindow(Type type)
		{
			return IsContains(type.FullName);
		}


		/// <summary>
		/// 异步打开窗口
		/// </summary>
		/// <param name="location">资源定位地址</param>
		/// <param name="userDatas">用户自定义数据</param>
		public static UniTask OpenWindowAsync<T>(string location, params System.Object[] userDatas) where T : UIWindow
		{
			return OpenWindowAsync(typeof(T), location, userDatas);
		}

		private static UniTask OpenWindowAsync(Type type, string location, params System.Object[] userDatas)
		{
			string windowName = type.FullName;

			// 如果窗口已经存在
			if (IsContains(windowName) != null)
			{
				UIWindow window = GetWindow(windowName);
				Pop(window); //弹出窗口
				Push(window); //重新压入
				window.TryInvoke(OnWindowPrepare, userDatas);
			}
			else
			{
				UIWindow window = CreateInstance(type);
				Push(window); //首次压入
				return window.InternalLoad(location, OnWindowPrepare, userDatas);
			}

			return UniTask.CompletedTask;
		}


		private static void OpenWindowSync(Type type, string location, params System.Object[] userDatas)
		{
			string windowName = type.FullName;
			if (IsContains(windowName) != null)
			{
				UIWindow window = GetWindow(windowName);
				Pop(window); //弹出窗口
				Push(window); //重新压入
				window.TryInvoke(OnWindowPrepare, userDatas);
			}
			else
			{
				UIWindow window = CreateInstance(type);
				Push(window); //首次压入
				window.InternalLoadSync(location, OnWindowPrepare, userDatas);
			}
		}


		/// <summary>
		/// 同步打开窗口
		/// </summary>
		public static void OpenWindowSync<T>(string location, params object[] userDatas)
			where T : UIWindow
		{
			OpenWindowSync(typeof(T), location, userDatas);
		}


		/// <summary>
		/// 关闭窗口
		/// </summary>
		public static void CloseWindow<T>() where T : UIWindow
		{
			CloseWindow(typeof(T));
		}

		private static void CloseWindow(Type type)
		{
			string windowName = type.FullName;
			UIWindow window = GetWindow(windowName);
			if (window == null) return;
			window.InternalDestroy();
			Pop(window);
			OnSortWindowDepth(window.WindowLayer);
			OnSetWindowVisible();
		}

		/// <summary>
		/// 关闭所有窗口
		/// </summary>
		public static void CloseAll()
		{
			for (int i = 0; i < _stack.Count; i++)
			{
				UIWindow window = _stack[i];
				window.InternalDestroy();
			}

			_stack.Clear();
		}


		private static void OnWindowPrepare(UIWindow window)
		{
			OnSortWindowDepth(window.WindowLayer); //根据页面栈进行排序
			window.InternalCreate(); //内部生命周期
			window.InternalRefresh(); //内部生命周期
			OnSetWindowVisible(); //设置全屏页面可见性
		}

		/// <summary>
		/// 按照深度排序当前面板，修改Canvas上的排序等级
		/// </summary>
		/// <param name="layer"></param>
		private static void OnSortWindowDepth(int layer)
		{
			int depth = layer;
			for (int i = 0; i < _stack.Count; i++)
			{
				if (_stack[i].WindowLayer == layer)
				{
					_stack[i].Depth = depth;
					depth += 100; //注意：每次递增100深度
				}
			}
		}

		/// <summary>
		/// 找到第一个准备好且全屏的窗口
		/// </summary>
		private static void OnSetWindowVisible()
		{
			bool isHideNext = false;
			for (int i = _stack.Count - 1; i >= 0; i--)
			{
				UIWindow window = _stack[i];
				if (isHideNext == false)
				{
					window.Visible = true;
					if (window.IsPrepare && window.FullScreen)
						isHideNext = true;
				}
				else
				{
					window.Visible = false;
				}
			}
		}

		/// <summary>
		/// 实例化类，通过属性得到参数，赋予资源加载器
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
		/// <exception cref="Exception"></exception>
		private static UIWindow CreateInstance(Type type)
		{
			UIWindow window = Activator.CreateInstance(type) as UIWindow;
			WindowAttribute attribute = Attribute.GetCustomAttribute(type, typeof(WindowAttribute)) as WindowAttribute;
			if (window == null) throw new Exception($"Window {type.FullName} create instance failed.");
			if (attribute == null)
				throw new Exception($"Window {type.FullName} not found {nameof(WindowAttribute)} attribute.");
			window.Init(type.FullName, attribute.WindowLayer, attribute.FullScreen);
			window._assetLoader = UniAssetSystem.GetAssetLoader();
			return window;
		}

		private static UIWindow GetWindow(string name)
		{
			for (int i = 0; i < _stack.Count; i++)
			{
				UIWindow window = _stack[i];
				if (window.WindowName == name)
					return window;
			}

			return null;
		}

		private static UIWindow IsContains(string name)
		{
			for (int i = 0; i < _stack.Count; i++)
			{
				UIWindow window = _stack[i];
				if (window.WindowName == name)
					return window;
			}

			return null;
		}

		/// <summary>
		/// 在栈中寻找到同一个层级的位置并插入
		/// </summary>
		/// <param name="window"></param>
		/// <exception cref="Exception"></exception>
		private static void Push(UIWindow window)
		{
			// 如果已经存在
			if (IsContains(window.WindowName) != null) throw new Exception($"Window {window.WindowName} is exist.");
			//获取最后一个和当前页面层级相同的页面的栈位置，用于后续插入
			int insertIndex = -1;
			for (int i = 0; i < _stack.Count; i++)
			{
				if (window.WindowLayer == _stack[i].WindowLayer)
					insertIndex = i + 1;
			}

			// 如果没有所属层级，找到相邻层级
			if (insertIndex == -1)
			{
				for (int i = 0; i < _stack.Count; i++)
				{
					if (window.WindowLayer > _stack[i].WindowLayer)
						insertIndex = i + 1;
				}
			}

			// 如果是空栈或没有找到插入位置
			if (insertIndex == -1)
			{
				insertIndex = 0;
			}

			// 最后插入到堆栈
			_stack.Insert(insertIndex, window);
		}

		private static void Pop(UIWindow window)
		{
			// 从堆栈里移除
			_stack.Remove(window);
		}
	}
}